import { defineVirtualModule, generageImports } from './_utils'
import env from './env'

export default defineVirtualModule(
  'wasm-virutal:emscripten-helper',
  `
  ${generageImports([env, ['isBroswer', 'isNode', 'isWorker']])}

  /*#__PURE__*/ const ENVIRONMENT_IS_NODE = isNode;
  /*#__PURE__*/ const ENVIRONMENT_IS_WEB = isBroswer;
  /*#__PURE__*/ const ENVIRONMENT_IS_WORKER = isWorker;
  
  /*#__PURE__*/ const dataURIPrefix = "data:application/octet-stream;base64,";
  
  /*#__PURE__*/ const UTF8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf8") : undefined;
  /*#__PURE__*/ const UTF16Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-16le") : undefined;
  
  const isDataURI = filename => {
    return filename.startsWith(dataURIPrefix);
  };
  
  const isFileURI = filename => {
    return filename.startsWith("file://");
  };

  const typeMapping = /*#__PURE__*/ [
    Int8Array,
    Uint8Array,
    Int16Array,
    Uint16Array,
    Int32Array,
    Uint32Array,
    Float32Array,
    Float64Array,
  ]
    
  const UTF8ArrayToString = (heap, idx, maxBytesToRead) => {
    let endIdx = idx + maxBytesToRead;
    let endPtr = idx;
    while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr;
    if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) {
      return UTF8Decoder.decode(heap.subarray(idx, endPtr));
    } else {
      let str = "";
      while (idx < endPtr) {
        let u0 = heap[idx++];
        if (!(u0 & 128)) {
          str += String.fromCharCode(u0);
          continue;
        }
        let u1 = heap[idx++] & 63;
        if ((u0 & 224) == 192) {
          str += String.fromCharCode(((u0 & 31) << 6) | u1);
          continue;
        }
        let u2 = heap[idx++] & 63;
        if ((u0 & 240) == 224) {
          u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
        } else {
          u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63);
        }
        if (u0 < 65536) {
          str += String.fromCharCode(u0);
        } else {
          let ch = u0 - 65536;
          str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023));
        }
      }
      return str;
    }
  };
  
  const stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => {
    if (!(maxBytesToWrite > 0)) return 0;
    let startIdx = outIdx;
    let endIdx = outIdx + maxBytesToWrite - 1;
    for (var i = 0, len = str.length; i < len; ++i) {
      let u = str.charCodeAt(i);
      if (u >= 55296 && u <= 57343) {
        let u1 = str.charCodeAt(++i);
        u = (65536 + ((u & 1023) << 10)) | (u1 & 1023);
      }
      if (u <= 127) {
        if (outIdx >= endIdx) break;
        heap[outIdx++] = u;
      } else if (u <= 2047) {
        if (outIdx + 1 >= endIdx) break;
        heap[outIdx++] = 192 | (u >> 6);
        heap[outIdx++] = 128 | (u & 63);
      } else if (u <= 65535) {
        if (outIdx + 2 >= endIdx) break;
        heap[outIdx++] = 224 | (u >> 12);
        heap[outIdx++] = 128 | ((u >> 6) & 63);
        heap[outIdx++] = 128 | (u & 63);
      } else {
        if (outIdx + 3 >= endIdx) break;
        heap[outIdx++] = 240 | (u >> 18);
        heap[outIdx++] = 128 | ((u >> 12) & 63);
        heap[outIdx++] = 128 | ((u >> 6) & 63);
        heap[outIdx++] = 128 | (u & 63);
      }
    }
    heap[outIdx] = 0;
    return outIdx - startIdx;
  };
  
  const lengthBytesUTF8 = str => {
    let length = 0;
    for (var i = 0, len = str.length; i < len; ++i) {
      let u = str.charCodeAt(i);
      if (u >= 55296 && u <= 57343) u = (65536 + ((u & 1023) << 10)) | (str.charCodeAt(++i) & 1023);
      if (u <= 127) ++length;
      else if (u <= 2047) length += 2;
      else if (u <= 65535) length += 3;
      else length += 4;
    }
    return length;
  };
  
  const lengthBytesUTF16 = str => {
    return str.length * 2;
  };
  
  const lengthBytesUTF32 = str => {
    let length = 0;
    for (var i = 0, len = str.length; i < len; ++i) {
      let codeUnit = str.charCodeAt(i);
      if (codeUnit >= 55296 && codeUnit <= 57343) ++i;
      length += 4;
    }
    return length;
  };
  
  const getShiftFromSize = size => {
    switch (size) {
      case 1:
        return 0;
      case 2:
        return 1;
      case 4:
        return 2;
      case 8:
        return 3;
      default:
        throw new TypeError("Unknown type size: " + size);
    }
  };
  
  const char_0 = 48, char_9 = 57;
  
  const makeLegalFunctionName = name => {
    if (undefined === name) {
      return "_unknown";
    }
    name = name.replace(/[^\w]/g, "$");
    var f = name.charCodeAt(0);
    if (f >= char_0 && f <= char_9) {
      return "_" + name;
    } else {
      return name;
    }
  };
  
  /*#__PURE__*/ const Object_defineProperty = Object.defineProperty;
  
  const createNamedFunction = (name, body) => {
    const func = function (...args) {
      return body.apply(this, args);
    };
    Object_defineProperty(func, "name", { value: makeLegalFunctionName(name), configurable: true });
    return func;
  };
  
  function new_(constructor, argumentList) {
    if (!(constructor instanceof Function)) {
      throw new TypeError("new_ called with constructor type " + typeof constructor + " which is not a function");
    }
    var dummy = createNamedFunction(constructor.name || "unknownFunctionName", function () {});
    dummy.prototype = constructor.prototype;
    var obj = new dummy();
    var r = constructor.apply(obj, argumentList);
    return r instanceof Object ? r : obj;
  }
  
  let read_, readAsync, readBinary, setWindowTitle;
  
  if (/*#__PURE__*/ ENVIRONMENT_IS_NODE) {
    let nodeFS_readFileSync, nodeFS_readFile, nodePath_normalize;
    read_ = (filename, binary) => {
      if (!nodeFS_readFileSync) {
        ({ readFileSync: nodeFS_readFileSync, readFile: nodeFS_readFile } = require("fs"));
      }
      if (!nodePath_normalize) nodePath_normalize = require("path").normalize;
      filename = nodePath_normalize(filename);
      return nodeFS_readFileSync(filename, binary ? null : "utf8");
    };
    readBinary = filename => {
      var ret = read_(filename, true);
      if (!ret.buffer) {
        ret = new Uint8Array(ret);
      }
      return ret;
    };
    readAsync = (filename, onload, onerror) => {
      if (!nodeFS_readFile) {
        ({ readFileSync: nodeFS_readFileSync, readFile: nodeFS_readFile } = require("fs"));
      }
      if (!nodePath_normalize) nodePath_normalize = require("path").normalize;
      filename = nodePath_normalize(filename);
      nodeFS_readFile(filename, function (err, data) {
        if (err) onerror(err);
        else onload(data.buffer);
      });
    };
  } else if (/*#__PURE__*/ ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
    read_ = url => {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", url, false);
      xhr.send(null);
      return xhr.responseText;
    };
    if (/*#__PURE__*/ ENVIRONMENT_IS_WORKER) {
      readBinary = url => {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url, false);
        xhr.responseType = "arraybuffer";
        xhr.send(null);
        return new Uint8Array(xhr.response);
      };
    }
    readAsync = (url, onload, onerror) => {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", url, true);
      xhr.responseType = "arraybuffer";
      xhr.onload = () => {
        if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) {
          onload(xhr.response);
          return;
        }
        onerror();
      };
      xhr.onerror = onerror;
      xhr.send(null);
    };
  
    setWindowTitle = title => {
      document.title = title;
    };
  } else {
  }  
  `,
  [
    'ENVIRONMENT_IS_NODE',
    'ENVIRONMENT_IS_WEB',
    'ENVIRONMENT_IS_WORKER',
    'dataURIPrefix',
    'UTF8Decoder',
    'UTF16Decoder',
    'isDataURI',
    'isFileURI',
    'UTF8ArrayToString',
    'stringToUTF8Array',
    'lengthBytesUTF8',
    'lengthBytesUTF16',
    'lengthBytesUTF32',
    'getShiftFromSize',
    'makeLegalFunctionName',
    'createNamedFunction',
    'new_',
    'read_',
    'readAsync',
    'readBinary',
    'setWindowTitle',
    'typeMapping'
  ]
)
